package com.myrslok;


import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.BuildListener;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Javac;
import org.apache.tools.ant.types.Path;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;

public class JavaCompiler {

    private static final Logger logger = LoggerFactory.getLogger(JavaCompiler.class);
    private static final Logger javacLogger = LoggerFactory.getLogger(Javac.class);
    private Project project;

    /**
     * Instantiates the compiler and holds reference to the file manager as recommended by javadoc.
     */
    public JavaCompiler() {
    }

    public CompilationResult compile(@NotNull final CompileRequest config) {
        final File destDir = new File(config.getTargetDir());
        if (destDir.mkdirs()) {
            logger.info("Created dir {}", destDir);
        }

        final Javac javac = new Javac();
        project = new Project();
        final Path sourcepath = new Path(project);

        for (final String srcDir : config.getSrcDirs()) {
            final File srcDirF = new File(srcDir);
            if (srcDirF.exists()) {
                sourcepath.add(new Path(project, srcDir));
            } else {
                logger.warn("src dir {} does not exist", srcDirF);
            }
        }

        javac.setProject(project);
        javac.setSrcdir(sourcepath);
        javac.setDestdir(destDir);
        javac.setClasspath(buildClassPath(config));
        project.addBuildListener(new CompilerLogger());
        logSettings(javac);
        javac.execute();

        return new CompilationResult();
    }

    private void logSettings(Javac javac) {
        logPath("Sourcedir:", javac.getSrcdir());
        logPath("Classpath:", javac.getClasspath());
    }

    private void logPath(String preamble, Path path) {
        logger.debug(preamble);
        if (path == null || path.list().length == 0) {
            logger.debug("    + no entries");
        } else {
            for (String pathEntry : path.list()) {
                logger.debug("    + {}", pathEntry);
            }
        }
    }

    private Path buildClassPath(@NotNull final CompileRequest config) {
        final Path classPath = new Path(project);
        for (final File file : config.getClassPath()) {
            classPath.add(new Path(project, file.getPath()));
        }
        return classPath;
    }

    /**
     * Logs the compiler messages using the standard logger.
     */
    private static class CompilerLogger implements BuildListener {
        @Override
        public void buildStarted(final BuildEvent event) {
        }

        @Override
        public void buildFinished(final BuildEvent event) {
        }

        @Override
        public void targetStarted(final BuildEvent event) {
        }

        @Override
        public void targetFinished(final BuildEvent event) {
        }

        @Override
        public void taskStarted(final BuildEvent event) {
        }

        @Override
        public void taskFinished(final BuildEvent event) {
        }

        @Override
        public void messageLogged(final BuildEvent event) {
            switch (event.getPriority()) {
                case Project.MSG_INFO:
                    javacLogger.info(event.getMessage());
                    break;
                case Project.MSG_ERR:
                    javacLogger.error(event.getMessage());
                    break;
                case Project.MSG_DEBUG:
                    javacLogger.debug(event.getMessage());
                    break;
                case Project.MSG_WARN:
                    javacLogger.warn(event.getMessage());
                    break;
                case Project.MSG_VERBOSE:
                    javacLogger.debug(event.getMessage());
                    break;
            }
        }
    }
}
